﻿#ifndef QLIB_HEADER_POLYGON
#define QLIB_HEADER_POLYGON

#include "qlib/core/array.h"
#include "qlib/core/object.h"
#include "qlib/core/vec2.h"
#include "qlib/layout/property.h"
#include "qlib/layout/repetition.h"

#include <algorithm>
#include <cfloat>
#include <cstdint>
#include <entt/entt.hpp>
#include <memory>
#include <vector>

namespace qlib {
enum struct GeoPartType;

class Polygon;
using PolygonPtrArray = std::vector<std::shared_ptr<Polygon>>;
using PolygonPtrArrayMap = std::unordered_map<Tag, PolygonPtrArray, Tag::Hash>;
using GeoRegMap = std::unordered_map<Tag, entt::registry, Tag::Hash>;

// 0: 中心， 1: 顶点， 2: 边， 3: 边中点，4: box顶点, 5: box边, 6: box边中心
using PolygonParts = std::tuple<Vec2d, Vec2dArray, std::vector<Vec2dArray>, Vec2dArray, Vec2dArray, std::vector<Vec2dArray>, Vec2dArray>;

/**
 * @brief 多边形几何对象
 */
class QLIB_API Polygon {
 public:
  Polygon();

  Polygon(const Polygon& oth);
  Polygon& operator=(const Polygon& other);

  using s_ptr = std::shared_ptr<Polygon>;
  template <typename... Args>
  static s_ptr create(Args... args) {
    return std::make_shared<Polygon>(args...);
  }
  // template <>
  static s_ptr create() {
    return std::make_shared<Polygon>();
  }
  static s_ptr create(const Polygon& value) { return std::make_shared<Polygon>(value); }

  s_ptr copy() const;

  /**
   * @brief 应用重复产生多边形
   * 
   * @param[out] result 返回多边形数组 
   */
  void apply_repetition(PolygonPtrArray& result);
  void apply_repetition(GeoRegMap& result);

  /// @brief 多边形面积
  double area() const;
  double signed_area() const;

  /**
   * @brief 获取多边形的包围盒
   * 
   * @return[out] Vec2dArray 元素[0]表示包围盒左下角，[1]表示右上角 
   * @code {.c++}
   * Polygon::s_ptr 
   * @endcode
   * 
   */
  Vec2dArray bounding_box();
  Vec2dArray bounding_box_points() { return box_to_points(bounding_box()); };

  /**
   * @brief 检查点是否在此多边形内
   * 
   * @param[in] point 点
   * @return true 不在多边形内
   * @return false 在多边形内
   */
  bool contain(const Vec2d& point) const;

  /**
   * @brief 检查多个点是否在此多边形内
   * 
   * @param[in] points 点数组
   * @param[out] res 每个点是否在多边形内
   */
  void contain(const Vec2dArray& points, std::vector<bool>& res);

  /**
   * @brief 检查是否所有点都在这个多边形内
   * 
   * @param[in] points 点数组
   * @return true  所有点都在多边形内
   * @return false 存在点不在多边形内
   */
  bool contain_all(const Vec2dArray& points);
  bool contain_any(const Vec2dArray& points);

  /**
   * @brief 给多边形的角加圆角
   * 
   * @param[in] radii 半径
   * @param[in] tolerance 公差
   */
  void fillet(const std::vector<double>& radii, double tolerance);

  /**
   * @brief 拆分多边形
   * 
   * @param[in] max_points  多边形拥有的最大点数
   * @param[in] precision   精度   
   * @param[out] result 拆分得到的多边形数组      
   */
  void fracture(uint64_t max_points, double precision, PolygonPtrArray& result) const;

  /// @brief 沿着po-p1镜像多边形
  void mirror(Vec2d p0, Vec2d p1);

  /**
   * @brief 计算多边形周长
   * 
   * @return double 周长
   */
  double perimeter() const;

  /**
   * @brief 缩放多边形
   * 
   * @param[in] sx x轴缩放系数
   * @param[in] sy y轴缩放系数
   * @param[in] center 缩放原点
   */
  void scale(double sx, double sy, Vec2d center);

  /**
   * @brief 旋转多边形
   * 
   * @param[in] angle  旋转角度
   * @param[in] center 旋转中心
   */
  void rotate(double angle, Vec2d center);

  /**
   * @brief 位移多边形
   * 
   * @param[in] dx x轴偏移量
   * @param[in] dy y轴偏移量
   */
  void translate(double dx, double dy);

  /**
   * @brief 变换多边形
   * 
   * @param[in] magnification 缩放系数
   * @param[in] x_reflection  是否沿着x轴镜像
   * @param[in] rotation      旋转角度
   * @param[in] origin        旋转中心
   */
  void transform(double magnification, bool x_reflection, double rotation, Vec2d translate);

  Vec2d get_center_pos();
  void set_center_pos(Vec2d center);

  // std::vector<Vec2dArray> get_edges();  // 获取所有边
  // Vec2dArray get_edges_mid();           // 获取边中心点

  uint32_t getLayer();
  void setLayer(uint32_t layer);

  PolygonParts get_parts();
  Vec2dArray query_parts(GeoPartType type, uint32_t id);

  // 渲染
  Vec2dArray render_aabb();
  Vec2dArray render_poly();
  Vec2dArray render_absorp();

  void genTriIndices();

  Vec2dArray box;
  // Polygon::s_ptr to_polygon();

  uint32_t layer;     ///< 多边形图层
  uint32_t datatype;  ///< 多边形类型

  Vec2dArray points;                  ///< 顶点数组
  std::vector<uint32_t> tri_indices;  ///< 三角化索引

  PropertyArray properties;  ///< 属性对象
  Repetition repetition;     ///< 重复对象

  void* owner;
};

}  // namespace qlib

#endif